﻿// Copyright © Microsoft Corporation.  All Rights Reserved.
// This code released under the terms of the 
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.) [INCLUDE APACHE 2.0 AS AN OPTION.]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.PerformancePoint.Scorecards.DesignerWorkspace;
using Microsoft.PerformancePoint.Scorecards;
using System.Xml.Serialization;
using System.Xml;
using ppscd.Configuration;
using System.IO;
using ppscd;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;

namespace ppscd
{
    public class ContentDeployer
    {
        private static string mUniquePropertyPrefix = "ImportGUID_";        

        public void Deploy(string workspaceFile)
        {
            if (!File.Exists(workspaceFile))
            {
                // todo - refactor exceptions
                throw new FileNotFoundException("The required workspace file \"" + workspaceFile + "\" does not exist.");
            }

            // where does this fit?
            if (String.IsNullOrEmpty(PerformancePointSettings.Current.DestinationServer))
            {
                throw new Exception("The DestinationServer is null or empty. Please enter a valid destination server such as \"http://yoursharepointserver/\"");
            }

            var importerObject = new Importer();

            // changed from /BICenter/ to /
            // PerformancePointSettings.Current.WebPath = "/";

            var settings = new PerformancePointSettings();

            var model = Deserialize<ScorecardModel>(File.ReadAllText(workspaceFile));
                        
            var importGUID = Guid.NewGuid().ToString();

            foreach (var element in model.ElementsInWorkspace.Elements)
            {
                // remove any previous import guids
                var propertiesToRemove = new List<string>();

                foreach (BpmProperty property in element.Properties)
                {
                    if (property.UniqueName != null && property.UniqueName.StartsWith(mUniquePropertyPrefix))
                    {
                        propertiesToRemove.Add(property.UniqueName);
                    }
                }

                foreach (var property in propertiesToRemove)
                {
                    element.Properties.Remove(property);
                }

                var importguid = new BpmPropertyText();
                importguid.UniqueName = FormatUniquePropertyName(importGUID);
                importguid.Text = Guid.NewGuid().ToString();
                importguid.Visible = false;

                element.Properties.Add(importguid);

                //ScorecardModel.GetInstance().ElementsOnServer.AddElement(element);
            }

            foreach (RepositoryList list in model.RepositoryListsInWorkspace)
            {
                settings.Locations.Add(new Replacement(list.Location.ListUrl, String.Empty));
            }

            // bug 6 - make sure we generate temp ddwx instead of overwriting current ddwx
            // because Dashboard Designer has issues loading the ddwx after we modify it
            var tempWorkspaceFile = Path.GetTempFileName();

            Console.WriteLine("Generating temporary ddwx file at " + tempWorkspaceFile);

            File.WriteAllText(tempWorkspaceFile, Serialize<ScorecardModel>(model));

            importerObject.InitializeImport(tempWorkspaceFile, PerformancePointSettings.Current.SiteCollectionPath);

            bool removeOldItems = false;
            if (Boolean.TryParse(PerformancePointSettings.Current.RemoveOldPerformancePointItems, out removeOldItems) && removeOldItems)
            {
                //we call the method bellow before executing the import.
                SharepointHandler.Current.PopulateCurrentSharepointItems(importerObject.GetPerformancePointItemsDeployLocation());
                SharepointHandler.Current.RemoveOldItems();
            }

            //This was needed to test why the objects are doubled on every deploy made
            //importerObject.TestMicrosoftInternalCode(model);

            var elements = importerObject.Import();
           
            try
            {
                File.Delete(tempWorkspaceFile);
            }
            catch { }

            // We could either call GetListItems or just add the elements returned by Import
            // which is faster.
            foreach (var element in elements)
            {
                ScorecardModel.GetInstance().ElementsOnServer.AddElement(element);
            }

            foreach (var element in elements)
            {
                if (element is ReportView)
                {
                    HandleReport(element as ReportView, settings);
                }

                if (element is DataSource)
                {
                    HandleDataSource(element, settings);
                }

                if (element is Dashboard)
                {
                    HandleDashboard(model, element, importGUID, settings);
                }
            }

            if (removeOldItems)
            {
                SharepointHandler.Current.ReStoreHomePage();
            }

            //File.WriteAllText("test.settings", Serialize<PerformancePointSettings>(settings));
        }
        
        private static string FormatUniquePropertyName(string guid)
        {
            return mUniquePropertyPrefix + guid;
        }

        private static void HandleDataSource(FirstClassElement element, PerformancePointSettings settings)
        {
            var dataSource = element as DataSource;

            if (dataSource.UseConnectionString)
            {
                Replacement.R<DataSource>(dataSource, d => d.ConnectionString, PerformancePointSettings.Current.DataSources.ConnectionString);
            }

            Replacement.R<DataSource>(dataSource, d => d.ServerName, PerformancePointSettings.Current.DataSources.ServerName);

            if (dataSource.SourceName.Equals(DataSourceNames.Adomd))
            {
                Replacement.R<DataSource>(dataSource, d => d.ServerName, PerformancePointSettings.Current.DataSources.AnalysisServices.ServerName);
                Replacement.R<DataSource>(dataSource, d => d.DatabaseName, PerformancePointSettings.Current.DataSources.AnalysisServices.DatabaseName);
                Replacement.R<DataSource>(dataSource, d => d.CubeName, PerformancePointSettings.Current.DataSources.AnalysisServices.CubeName);
            }

            if (dataSource.SourceName.Equals(DataSourceNames.ExcelServicesWorkbook))
            {
                // ServerName="http://boi-ppssp1/sites/bi/"
                Replacement.R<DataSource>(dataSource, d => d.ServerName, PerformancePointSettings.Current.DataSources.ExcelServices.SharePointSite);

                // DatabaseName="Documents"
                Replacement.R<DataSource>(dataSource, d => d.DatabaseName, PerformancePointSettings.Current.DataSources.ExcelServices.DocumentLibrary);

                // CubeName="/sites/bi/Documents/Excel Services Sample Workbook.xlsx"
                Replacement.R<DataSource>(dataSource, d => d.CubeName, PerformancePointSettings.Current.DataSources.ExcelServices.ExcelWorkbook);

            }

            if (dataSource.SourceName.Equals(DataSourceNames.SqlTabular))
            {
                // ServerName="cubes4" DatabaseName="AdventureWorksDW2008" CubeName="[dbo].[DimProductCategory]" CubeDisplayName="dbo.DimProductCategory"
                Replacement.R<DataSource>(dataSource, d => d.ServerName, PerformancePointSettings.Current.DataSources.SQLServer.ServerName);
                Replacement.R<DataSource>(dataSource, d => d.DatabaseName, PerformancePointSettings.Current.DataSources.SQLServer.DatabaseName);
                Replacement.R<DataSource>(dataSource, d => d.CubeName, PerformancePointSettings.Current.DataSources.SQLServer.TableName);

                // TODO - CubeDisplayName
            }

            if (dataSource.SourceName.Equals(DataSourceNames.SharePointList))
            {
                // ServerName="http://boi-ppssp1/sites/bi/" DatabaseName="All Lists" CubeName="{627625D3-5392-4247-99FF-DFC1E0BCFE5B}" CubeDisplayName="Documents"
                Replacement.R<DataSource>(dataSource, d => d.ServerName, PerformancePointSettings.Current.DataSources.SharePointList.SharePointSite);
                Replacement.R<DataSource>(dataSource, d => d.DatabaseName, PerformancePointSettings.Current.DataSources.SharePointList.SharePointSiteList);
                Replacement.R<DataSource>(dataSource, d => d.CubeName, PerformancePointSettings.Current.DataSources.SharePointList.List);

                // TODO - this might require a web service call to look up the list GUID
            }

            RemoveImportGUID(dataSource);

            PerformancePointSettings.Current.GetAuthoringService().UpdateDataSource(dataSource);
        }

        private static void HandleDashboard(ScorecardModel model, FirstClassElement element, string importGUID, PerformancePointSettings settings)
        {
            Console.WriteLine("Updating dashboard \"" + element.Name + "\"");

            var dashboard = element as Dashboard;

            // model.DashboardsInWorkspace isn't updated
            foreach (var dashboardFromDDWX in model.ElementsInWorkspace.Elements.OfType<Dashboard>())
            {
                var dashboardImportGUID = (dashboard.Properties[FormatUniquePropertyName(importGUID)] as BpmPropertyText).Text;
                var dashboardFromDDWXImportGUID = (dashboardFromDDWX.Properties[FormatUniquePropertyName(importGUID)] as BpmPropertyText).Text;

                if (dashboardImportGUID.Equals(dashboardFromDDWXImportGUID))
                {
                    // Deployment paths are not imported, so we automatically grab the deployment path
                    // from the ddwx file
                    var deploymentPath = dashboardFromDDWX.DeploymentPath;

                    //Console.WriteLine("Deploying dashboard \"" + dashboard.Name + "\" to " + dashboardFromDDWX.DeploymentPath);

                    // Update Deployment Path
                    dashboard.DeploymentPath = Replacement.ReplaceFirst(deploymentPath, PerformancePointSettings.Current.Dashboards.DeploymentPath);

                    if (String.IsNullOrEmpty(dashboard.DeploymentPath))
                    {
                        Console.WriteLine("WARNING: dashboard \"" + dashboardFromDDWX.Name + "\" is missing the deployment path. Skipping deployment of this dashboard.");
                        continue;
                    }

                    Console.WriteLine("Previous master page path is " + dashboardFromDDWX.MasterPagePath);
                    dashboard.MasterPagePath = Replacement.ReplaceFirst(dashboardFromDDWX.MasterPagePath, PerformancePointSettings.Current.Dashboards.MasterPagePath);
                    Console.WriteLine("Updated master page path is " + dashboard.MasterPagePath);

                    dashboard.SitePath = Replacement.ReplaceFirst(dashboardFromDDWX.SitePath, PerformancePointSettings.Current.Dashboards.SitePath);

                    if (PerformancePointSettings.Current.Dashboards.AutomaticallyDeploy)
                    {
                        Console.WriteLine("Deploying dashboard \"" + dashboard.Name + "\"");

                        var deployer = new DashboardDeployer();

                        // The web URL is server + site collection path + web path

                        var webUrl = UrlUtils.Combine(PerformancePointSettings.Current.DestinationServer, dashboard.SitePath);


                        //UrlUtils.Combine(PerformancePointSettings.Current.DestinationServer, dashboard.SitePath);

                        var dashboardUri = deployer.Deploy(dashboard, webUrl, dashboard.DeploymentPath, dashboard.MasterPagePath);

                        Console.WriteLine("Deployed dashboard to " + dashboardUri);
                    }

                    RemoveImportGUID(dashboard);

                    PerformancePointSettings.Current.GetAuthoringService().UpdateDashboard(dashboard);

                    break;
                }
            }

            //Uri uri = Deploy(element as Dashboard);

            //// Dashboard Uris are relative, for example /BICenter/Dashboards/Adventure Works/Page 1.aspx
            //// Re-create the Uri using the full SharePointUrl so that this URI can be passed to
            //// an internet browser such as WebAuto.
            //uri = new Uri(new Uri(TestContext.Current.SharePointUrl), uri);

            //Log.Comment("Deployed dashboard " + element.Name.Text + " to " + uri);
            //dashboardUris.Add(uri);
            settings.Dashboards.DeploymentPath.Add(new Replacement(dashboard.DeploymentPath, String.Empty));
            settings.Dashboards.MasterPagePath.Add(new Replacement(dashboard.MasterPagePath, String.Empty));
            settings.Dashboards.SitePath.Add(new Replacement(dashboard.SitePath, String.Empty));
        }

        /// <summary>
        /// Remove the Import GUID before we save the element
        /// </summary>
        /// <param name="element"></param>
        private static void RemoveImportGUID(FirstClassElement element)
        {
            var propertiesToRemove = new List<string>();

            foreach (BpmProperty property in element.Properties)
            {
                if (property.UniqueName != null && property.UniqueName.StartsWith(mUniquePropertyPrefix))
                {
                    propertiesToRemove.Add(property.UniqueName);
                }
            }

            foreach (var property in propertiesToRemove)
            {
                element.Properties.Remove(property);
            }
        }

        private void HandleReport(ReportView reportView, PerformancePointSettings settings)
        {
            switch (reportView.TypeName)
            {
                case ReportViewNames.ExcelServices:

                    var excelServicesData = Deserialize<ExcelServicesData>(reportView.CustomData);


                    //                    <?xml version="1.0" encoding="utf-16"?>
                    //<ExcelServicesData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
                    // SharePointSite="http://boi-ppssp1/bi/" DocumentLibrary="dd8a7b2c-cb21-47f2-9e64-93042aa956af" 
                    // DocumentLibraryRootFolder="/bi/Documents" DocumentLibraryDisplay="Documents" FolderFileName="/bi/Documents/ES Demo.xlsx"
                    // FileName="ES Demo.xlsx" ItemName="DeterministicData" ViewOptions="AllowInteractivity AllowParameterModification AllowSorting 
                    // AllowFiltering AllowHyperlinks AllowPivotSpecificOperations AllowPeriodicRefresh AllowScratchPadMode" ToolbarType="None" 
                    // ToolbarOptions="AllowInExcelOperations AllowRecalculation">
                    //  <WorkbookParameters>
                    //    <Keys>
                    //      <string>SeedValue</string>
                    //    </Keys>
                    //    <Values>
                    //      <ArrayOfString />
                    //    </Values>
                    //  </WorkbookParameters>
                    //</ExcelServicesData>

                    // Copy the production logic here
                    var workbookUri = new Uri(new Uri(excelServicesData.SharePointSite), excelServicesData.FolderFileName).ToString();

                    workbookUri = Replacement.ReplaceFirst(workbookUri, PerformancePointSettings.Current.Reports.ExcelServicesReports.WorkbookUri);

                    excelServicesData.FolderFileName = new Uri(workbookUri).PathAndQuery;

                    // scary logic
                    workbookUri = workbookUri.Substring(0, workbookUri.LastIndexOf("/"));
                    workbookUri = workbookUri.Substring(0, workbookUri.LastIndexOf("/") + 1);

                    excelServicesData.SharePointSite = workbookUri;

                    reportView.CustomData = Serialize<ExcelServicesData>(excelServicesData);

                    break;

                case ReportViewNames.AnalyticChart:


                    break;

                case ReportViewNames.Url:
                    // <CustomData>http://www.bing.com</CustomData>
                    settings.Reports.WebPages.URL.Add(new Replacement(reportView.CustomData, String.Empty));
                    Replacement.R<ReportView>(reportView, m => m.CustomData, PerformancePointSettings.Current.Reports.WebPages.URL);
                    break;

                case ReportViewNames.PasPage:
                    //             <CustomData>&lt;?xml version="1.0" encoding="utf-16"?&gt;
                    // &lt;PasReportViewData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ServerUrl="http://boi-pas/pas/" PageId="1be272e2-3d64-4891-9995-4099a78be2ee"
                    // PagePath="Josh\Untitled\Reseller Sales Amount (Account Level 01)" ConfigOptions="" /&gt;</CustomData>

                    var pasReportViewData = Deserialize<PasReportViewData>(reportView.CustomData);

                    settings.Reports.ProClarityReports.ServerURL.Add(new Replacement(pasReportViewData.ServerUrl, String.Empty));

                    Replacement.R<PasReportViewData>(pasReportViewData, p => p.ServerUrl, PerformancePointSettings.Current.Reports.ProClarityReports.ServerURL);

                    reportView.CustomData = Serialize<PasReportViewData>(pasReportViewData);
                    //HandleProClarityReport(reportView);
                    break;

                case ReportViewNames.SqlReport:
                    //throw new NotImplementedException();
                    //<CustomData>&lt;?xml version="1.0" encoding="utf-16"?&gt;
                    //&lt;SqlReportViewData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ServerAddress="http://boi-sql2008/reportserver" ServerUrl="http://cubes4/reportserver/" ReportUrl="http://boi-ppssp1/" ReportMode="SharepointIntegrated" ShowToolbar="true" ShowParameters="false" ShowDocMap="false" Zoom="100" Format="HTML4.0" /&gt;</CustomData>

                    // TODO - log warning
                    if (reportView.CustomData == null)
                    {
                        break;
                    }

                    var sqlReportViewData = Deserialize<SqlReportViewData>(reportView.CustomData);

                    // TODO - there is a difference between Integrated reports and Report Server

                    settings.Reports.ReportingServicesReports.ReportServerURL.Add(new Replacement(sqlReportViewData.ServerUrl, String.Empty));
                    settings.Reports.ReportingServicesReports.ReportURL.Add(new Replacement(sqlReportViewData.ReportUrl, String.Empty));

                    // TODO - Need test case for when ServerUrl and the difference between ServerUrl, ServerAddress, ReportPath, and ReportUrl
                    if (!String.IsNullOrEmpty(sqlReportViewData.ServerUrl))
                    {
                        Replacement.R<SqlReportViewData>(sqlReportViewData, m => m.ServerUrl, PerformancePointSettings.Current.Reports.ReportingServicesReports.ReportServerURL);
                    }

                    if (!String.IsNullOrEmpty(sqlReportViewData.ServerAddress))
                    {
                        Replacement.R<SqlReportViewData>(sqlReportViewData, m => m.ServerAddress, PerformancePointSettings.Current.Reports.ReportingServicesReports.ReportServerURL);
                    }

                    if (!String.IsNullOrEmpty(sqlReportViewData.ReportPath))
                    {
                        Replacement.R<SqlReportViewData>(sqlReportViewData, m => m.ReportPath, PerformancePointSettings.Current.Reports.ReportingServicesReports.ReportURL);
                    }

                    if (!String.IsNullOrEmpty(sqlReportViewData.ReportUrl))
                    {
                        Replacement.R<SqlReportViewData>(sqlReportViewData, m => m.ReportUrl, PerformancePointSettings.Current.Reports.ReportingServicesReports.ReportURL);
                    }

                    reportView.CustomData = Serialize<SqlReportViewData>(sqlReportViewData);
                    break;
                //default:
                //throw new NotImplementedException();
            }

            RemoveImportGUID(reportView);

            PerformancePointSettings.Current.GetAuthoringService().UpdateReportView(reportView);
        }

        private static string Serialize<T>(object data)
        {
            var serializer = new XmlSerializer(typeof(T));
            var customData = new StringBuilder();

            using (var stream = new StringWriter(customData))
            {
                serializer.Serialize(stream, data);
            }

            return customData.ToString();
        }

        private static T Deserialize<T>(string data)
        {
            var serializer = new XmlSerializer(typeof(T));

            object t = null;

            using (var stream = new StringReader(data))
            {
                t = serializer.Deserialize(stream);
            }

            return (T)t;
        }

        public void CreateSettingsFile(string workspaceFile)
        {
            var serializer = new XmlSerializer(typeof(ScorecardModel));

            ScorecardModel model = null;

            using (var reader = new XmlTextReader(workspaceFile))
            {
                model = serializer.Deserialize(reader) as ScorecardModel;
            }

            var settings = new PerformancePointSettings();

            settings.WorkspaceFile = workspaceFile;

            foreach (var element in model.ElementsInWorkspace.Elements)
            {
                if (element is ReportView)
                {
                    var reportView = (ReportView)element;

                    switch (reportView.TypeName)
                    {
                        case ReportViewNames.ExcelServices:
                            var excelServicesData = Deserialize<ExcelServicesData>(reportView.CustomData);
                            break;

                        case ReportViewNames.Url:
                            var url = reportView.CustomData;
                            settings.Reports.WebPages.URL.Add(new Replacement(url, String.Empty));
                            break;
                    }
                }

                if (element is DataSource)
                {
                    //HandleDataSource(element);
                }

                if (element is Dashboard)
                {
                    var dashboard = (Dashboard)element;

                    settings.Dashboards.DeploymentPath.Add(new Replacement(dashboard.DeploymentPath, String.Empty));
                    settings.Dashboards.MasterPagePath.Add(new Replacement(dashboard.MasterPagePath, String.Empty));
                    settings.Dashboards.SitePath.Add(new Replacement(dashboard.SitePath, String.Empty));

                    //HandleDashboard(model, element, importGUID);
                }
            }

            foreach (RepositoryList list in model.RepositoryListsInWorkspace)
            {
                settings.Locations.Add(new Replacement(list.Location.ListUrl, String.Empty));
            }

            File.WriteAllText("test.settings", Serialize<PerformancePointSettings>(settings));

        }
    }
}
